{$R-} {Range checking off} {$B+} {Boolean complete evaluation on} {$S+} {Stack checking on} {$I+} {I/O checking on} {$N-} {No numeric coprocessor} {$M 65500,16384,655360} {Turbo 3 default stack and heap} PROGRAM PASCAL_Pattern_Similarity_Program(INPUT,OUTPUT); {This program compares the GC pattern of an unknown sample} {with the GC patterns of some other known samples.} {The absolute difference between the peaks and the Euclidean distance} {are the two methods used to determine the similarity of the patterns.} {The sample data is stored in a file specified by user and} {the reference data in another file also specified by user} {The program reads in the sample data first.} {Then it reads in the reference data one at a time.} {The user can choose whichever peak he wants to normalize against.} {A lower value in the absolute difference or the Euclidean distance} {will indicate the patterns are more similar.} {The summary of results are recorded in an output file given by user.} CONST MaxPeak = 50; MaxArray = 40; TYPE String15 = STRING[15]; String14 = STRING[15]; String13 = STRING[13]; String6 = STRING[6]; GcPattern = RECORD SerialNo : String15; RunNo : String6; Time : ARRAY [1..MaxPeak] OF REAL; Peak : ARRAY [1..MaxPeak] OF REAL; NumPeak : INTEGER; Coeff : REAL END; ArrayOfPattern = ARRAY [1..MaxArray] OF GcPattern; MatchType = ARRAY [1..MaxPeak] OF BOOLEAN; VAR Reference,Sample,NormSample,RefSelect : GcPattern; GoodRef1,GoodRef2 : ArrayOfPattern; RefFile,SmpFile,Result,LongResult : TEXT; RefName,SmpName,Summary,Details : String14; Match : MatchType; Ready,Overflow,BadData,RefOK,NoDetail,Continue : BOOLEAN; Resolution,Coeff1,Coeff2 : REAL; Best,NormPeak,Count1,Count2,index : INTEGER; Answer : CHAR; PROCEDURE CheckReal (VAR InData : REAL; VAR IO : INTEGER); {This procedure checks if the typed-in data is a real number.} {If it is not, then there will be an I/O error and the program will bomb.} {This procedure will prevent the program from bombing from bad input data.} {Instead, it will come back to ask for the data once more} {until the appropriate type has been entered.} VAR GoodData : BOOLEAN; BEGIN GoodData := FALSE; WHILE NOT GoodData DO BEGIN WRITELN; WRITELN('Bad Data. Enter the number again.'); {$I-} READLN(Indata); {$I+} IO := IOResult; {! 1. IOResu^lt now returns different values corresponding to DOS error codes.} IF (IO<>0) OR (Indata<=0.0) THEN GoodData := FALSE ELSE GoodData := TRUE END END; PROCEDURE CheckInteger (VAR Indata, IO : INTEGER); {This procedure checks if the input data is an integer.} {If not, it prevents the program from bombing} {but keeping requesting for more until the input is an integer.} VAR GoodData : BOOLEAN; BEGIN GoodData := FALSE; WHILE NOT GoodData DO BEGIN WRITELN; WRITELN('Bad Data. Enter the number again.'); {$I-} READLN(Indata); {$I+} IO := IOResult; {! 2. IOResu^lt now returns different values corresponding to DOS error codes.} IF (IO<>0) OR (Indata<=0) THEN GoodData := FALSE ELSE GoodData := TRUE END END; PROCEDURE CheckSameName(VAR InName : String14; ExistName : String14; VAR SameName : BOOLEAN); {This procedure checks if the name for details is the same as} {the one for summary. If this is so, SameName is true.} BEGIN SameName := FALSE; IF InName = ExistName THEN SameName := TRUE END; PROCEDURE CheckNoName (Filename : String14; VAR NoName : BOOLEAN); {This procedure checks if a name has been entered.} {If not, NoName will be true.} VAR LengthOfName : INTEGER; BEGIN LengthOfName := LENGTH(Filename); IF LengthOfName <= 0 THEN NoName := TRUE ELSE NoName := FALSE END; PROCEDURE CheckBlank (Filename : String14; VAR HasBlank : BOOLEAN); {This procedure checks if there is any blank in the name entered.} {If there is any, the program will flag this as a mistake.} {The user will then asked to enter the name again.} CONST Blank = ' '; VAR i, LengthOfName : INTEGER; BEGIN LengthOfName := LENGTH(Filename); i := 0; HasBlank := FALSE; REPEAT i := i+1; IF Filename[i] = Blank THEN HasBlank := TRUE UNTIL HasBlank OR (i>=LengthOfName) END; PROCEDURE CheckFile (VAR Infile : TEXT; VAR Filename : String14; ExistName : String14; VAR IO : INTEGER); {This procedure checks if the input file exists.} {If it doesn't, the procedure will prevent the program from bombing.} {Instead, it will ask for the filename again until one exists.} VAR GoodData,SameName,NoName,HasBlank : BOOLEAN; BEGIN SameName := FALSE; NoName := FALSE; HasBlank := FALSE; GoodData := FALSE; WHILE NOT GoodData DO BEGIN CheckNoName(Filename,NoName); IF Not NoName THEN CheckBlank(Filename,HasBlank); IF (Not NoName) AND (Not HasBlank) THEN CheckSameName(Filename, Existname, SameName); IF (IO<>0) OR NoName OR HasBlank OR SameName THEN GoodData := FALSE ELSE GoodData := TRUE; IF Not GoodData THEN BEGIN WRITELN; IF SameName THEN WRITELN('Same name as sample file. Enter another name for reference file.') ELSE WRITELN('Input file does not exist. Enter the filename again.'); READLN(Filename); ASSIGN(Infile,Filename); {$I-} RESET(Infile); {$I+} IO := IOResult {! 3. IOResu^lt now returns different values corresponding to DOS error codes.} END END END; PROCEDURE CheckFileExist (VAR Filename : String14; VAR OK : BOOLEAN); {This procedure checks if the name of the output file already exists.} {If so, then FileExist will be true.} {The program will inform the user that file already exists.} {The user may decide to write over the existing file.} {If not, the program will ask the user to enter another name for the output file.} VAR DummyFile : TEXT; IO : INTEGER; FileExist : BOOLEAN; BEGIN OK := TRUE; FileExist := FALSE; ASSIGN(DummyFile,Filename); {$I-} RESET(DummyFile); {$I+} IO := IOResult; {! 4. IO^Result now returns different values corresponding to DOS error codes.} IF IO = 0 THEN BEGIN FileExist := TRUE; WRITELN; WRITELN('Output file already exists. Enter Y to write over the file.'); READLN(Answer); IF Answer IN ['y','Y'] THEN OK := TRUE ELSE BEGIN OK := FALSE; WRITELN('User does not wish to write over existing file.'); WRITELN('Enter another name for the output file.'); READLN(Filename) END END ELSE BEGIN {$I-} REWRITE(DummyFile); {$I+} IO := IOResult; {! 5. IOResu^lt now returns different values corresponding to DOS error codes.} IF IO <> 0 THEN BEGIN WRITELN; WRITELN('Bad Name. Enter again.'); READLN(Filename); OK := FALSE END END; {$I-} CLOSE(DummyFile); {I+} IO := IOResult; {! 6. IO^Result now returns different values corresponding to DOS error codes.} IF IO <> 0 THEN OK := FALSE END; PROCEDURE CheckSummaryName (VAR Summary : String14; SmpName,RefName : String14); {This procedure checks the name of the file entered by user.} {If no name is entered or there are blanks in the name,} {the program will flag this as a mistake.} {The user will be asked to enter the name again.} VAR SameName,NoName,HasBlank,OK : BOOLEAN; Answer : CHAR; BEGIN SameName := TRUE; NoName := TRUE; HasBlank := TRUE; OK := TRUE; REPEAT WHILE NoName OR HasBlank OR SameNAme DO BEGIN CheckSameName(Summary,SmpName,SameName); IF NOT SameName THEN CheckSameName(Summary,RefName,SameName); IF NOT SameName THEN CheckNoName(Summary,NoName); IF (NOT NoName) AND (NOT SameName) THEN CheckBlank(Summary,HasBlank); IF NoName OR HasBlank OR SameName THEN BEGIN WRITELN; WRITELN('Bad name. Enter the filename again.'); READLN(Summary) END END; CheckFileExist(Summary,OK); IF NOT OK THEN NoName := TRUE {Make sure new name is checked again} UNTIL OK; END; PROCEDURE CheckDetailName (VAR Details : String14; Summary,SmpName,RefName : String14); {This procedure checks the name given to store the details.} {If no name is given, or there is blanks in the name, or} {if the name is the same as the one for the summary.} {If this is so, the program will flag this as a mistake and} {the user will be asked to enter the name again.} VAR SameName,NoName,HasBlank,OK : BOOLEAN; BEGIN SameName := TRUE; NoName := TRUE; HasBlank := TRUE; OK := TRUE; REPEAT WHILE NoName OR HasBlank OR SameName DO BEGIN CheckSameName(Details,Summary,SameName); IF NOT SameName THEN CheckSameName(Details,RefName,SameName); IF NOT SameName THEN CheckSameName(Details,SmpName,SameName); IF NOT SameName THEN CheckNoName(Details,NoName); IF (NOT NoName) AND (NOT SameName) THEN CheckBlank(Details,HasBlank); IF NoName OR HasBlank OR SameName THEN BEGIN WRITELN; WRITELN('Bad Name. Enter the filename again.'); READLN(Details) END END; CheckFileExist(Details,OK); IF NOT OK THEN NoName := TRUE {Allow new name to be checked again.} UNTIL OK END; PROCEDURE Instruction (VAR Ready : BOOLEAN; VAR Resolution : REAL; VAR Best : INTEGER); {This procedure gives the instructions on how to set up input files.} {If the input files are not ready yet, the program will terminate.} {Otherwise, the user will give the size of the window for the retention time} {and the number of closest references he would like to list.} VAR Answer : CHAR; IO : INTEGER; BEGIN WRITELN; WRITELN('Do you need instructions on how to prepare the input files?'); READLN(Answer); WRITELN; IF Answer IN ['Y','y'] THEN BEGIN WRITELN('Before this program can be run, two input files need to be set up:'); WRITELN(' one for sample data and one for reference data.'); WRITELN; WRITELN('The format for the data in both files are the same:'); WRITELN; WRITELN('** columns 1-15 -- serial no. (e.g., iraq_sample_001)'); WRITELN; WRITELN('** columns 16-21 -- run no. (e.g., 210792)'); WRITELN; WRITELN('** next, enter the retention time and peak area in pairs,'); WRITELN(' separating each entry by a space.'); WRITELN(' (e.g., 22.87 312 23.39 327 49.00 2998 51.26 3800 -1 -1)'); WRITELN; WRITELN('** continue enter the data in one line'); WRITELN(' until the whole line (256 characters) is filled.'); WRITELN; WRITELN('** when the last entry has been entered,'); WRITELN(' follow this by two negative numbers.'); WRITELN(' (e.g., 22.87 312 23.39 327 49.00 2998 51.26 3800 -1 -1)'); WRITELN(' this is a flag to indicate the last entry is reached.'); WRITELN END; WRITELN('Are the input files ready to be processed?'); WRITELN('Enter Y for yes and N for no.'); READLN(Answer); WRITELN; IF Answer IN ['Y','y'] THEN BEGIN Ready := TRUE; WRITELN('The input files are ready and program will continue.'); WRITELN END ELSE BEGIN Ready := FALSE; WRITELN('Input files are not ready to be processed.'); WRITELN('Refer to the REFERENCE GUIDE on how to set up files.'); WRITELN END; IF Ready THEN BEGIN Resolution := -1.0; WRITELN; WRITELN('Enter the size of the window for the retention time.'); WRITELN('(The recommended size is between 0.05 to 0.1)'); {$I-} READLN(Resolution); {$I+} IO := IOResult; {! 7. IOResult^ now returns different values corresponding to DOS error codes.} IF (IO<>0) OR (Resolution<=0.0) THEN CheckReal(Resolution,IO); WRITELN; WRITELN('The size of the window is ',Resolution:5:2); WRITELN; Best := -1; WRITELN; WRITELN('Enter the number of the closest patterns you want to list.'); WRITELN('(The suggested values are between 10 to 30)'); {$I-} READLN(Best); {$I+} IO := IOResult; {! 8. IOResult^ now returns different values corresponding to DOS error codes.} IF (IO<>0) OR (Best<=0) THEN CheckInteger(Best,IO); IF Best > MaxArray THEN REPEAT WRITELN('The number you entered exceeds the allocations space which is ',MaxArray:3); WRITELN('Enter another number which is smaller than ',MaxArray:3); {$I-} READLN(Best); {$I+} IO := IOResult; {! 9. IOResult now^ returns different values corresponding to DOS error codes.} IF IO <> 0 THEN CheckInteger(Best,IO); UNTIL Best <= MaxArray; WRITELN; WRITELN(Output,Best:3,' closest patterns to the sample pattern will be listed.'); WRITELN END END; PROCEDURE GetFiles (VAR SmpFile,RefFile,Result,LongResult : TEXT; VAR SmpName,RefName,Summary,Details : String14; VAR NoDetail : BOOLEAN); {This procedure reads in the names of the input files and output files.} {The two input files are sample file and reference file.} {The two output files are one for summarization of results and one for details.} {The user may choose not to include the details for saving space.} VAR Answer : CHAR; IO : INTEGER; BEGIN WRITELN; WRITELN('Enter the name of the sample file.'); WRITELN('(A copy of trial sample data is stored in iraqsmp.txt)'); READLN(SmpName); ASSIGN(SmpFile,SmpName); {$I-} RESET(SmpFile); {$I+} IO := IOResult; {! 10. I^OResult now returns different values corresponding to DOS error codes.} CheckFile(SmpFile,SmpName,' ',IO); WRITELN; WRITELN('Enter the name of the reference file.'); WRITELN('(A copy of trial reference data is stored in iraqref.txt)'); READLN(RefName); ASSIGN(RefFile,RefName); {$I-} RESET(RefFile); {$I+} IO := IOResult; {! 11. I^OResult now returns different values corresponding to DOS error codes.} CheckFile(RefFile,RefName,SmpName,IO); WRITELN; WRITELN('Enter the name of the output file for the summary.'); WRITELN('(A suggested name is c;\iraqres.txt)'); READLN(Summary); CheckSummaryName(Summary,SmpName,RefName); ASSIGN(Result,Summary); REWRITE(Result); WRITELN; WRITELN('Do you want to store the details of the result?'); WRITELN('Enter Y for yes and N for no.'); READLN(Answer); IF Answer IN ['Y','y'] THEN BEGIN NoDetail := FALSE; WRITELN; WRITELN('Enter the name of the output file for details.'); WRITELN('(A suggested name is c:\iraqdet.txt)'); READLN(Details); CheckDetailName(Details,Summary,SmpName,RefName); ASSIGN(LongResult,Details); REWRITE(LongResult); WRITELN END ELSE BEGIN NoDetail := TRUE; Details := 'c:\junkfile.txt'; ASSIGN(LongResult,Details); REWRITE(LongResult); WRITELN END END; PROCEDURE WriteResult (VAR Result : TEXT; Sample : GcPattern; GoodRef1,GoodRef2 : ArrayOfPattern; Resolution : REAL; NormPeak, Count1,Count2 : INTEGER); {This procedure summarizes the result of this pattern comparison} {in the file as indicated by the user in the procedure GetFiles.} {Only the sample numbers and the run numbers and the coefficients} {of the closest references are included.} {The details of the results are stored in another file which is also} {indicated by the user in the procedure GetFiles.} VAR i : INTEGER; BEGIN IF Count1 > Best THEN Count1 := Best; IF Count2 > Best THEN Count2 := Best; WRITELN (Result); WRITELN (Result); WRITELN (Result,' >>>>>>>>>>>>>>>>>>>>SAMPLE<<<<<<<<<<<<<<<<<<< '); WRITELN (Result); WRITE (Result,'SAMPLE Serial Number : ',Sample.SerialNo); WRITELN (Result,' SAMPLE Run Number : ',Sample.RunNo); WRITELN (Result); WRITE (Result,'The Resolution is ',Resolution:5:2); WRITELN (Result,' and Peak ',NormPeak:3,' is chosen to be normalized.'); WRITELN (Result); WRITELN (Result); WRITELN (Result,'>>Result of Absolute Difference Test<<'); WRITELN (Result); FOR i := 1 TO Count1 DO BEGIN WRITE (Result,'The Coefficient is ',GoodRef1[i].Coeff:6:2); WRITE (Result,' Serial no.: ',GoodRef1[i].SerialNo); WRITELN (Result,' Run no.: ',GoodRef1[i].RunNo) END; WRITELN (Result); WRITELN (Result,'>>Result of Euclidean Distance Test<<'); WRITELN (Result); FOR i := 1 TO Count2 DO BEGIN WRITE (Result,'The Coefficient is ',GoodRef2[i].Coeff:6:2); WRITE (Result,' Serial no.: ',GoodRef2[i].SerialNo); WRITELN (Result,' Run no.: ',GoodRef2[i].RunNo) END; WRITELN (Result); WRITELN (Result) END; PROCEDURE WriteSample (VAR Result : TEXT; SmpData : GcPattern; Resolution : REAL; NormPeak : INTEGER); {This procedure writes the data of the sample to be compared,} {to the output file whose name is given by the user in procedure GetFiles.} {The data includes serial number, run number, the retention time and} {the peak area of each peak.} {The resolution of the retention time and the peak chosen to be} {normalized are also included.} VAR i : INTEGER; BEGIN WITH SmpData DO BEGIN WRITELN(Result); WRITELN(Result); WRITELN(Result,' >>>>>>>>>>>>>>>>>>>>SAMPLE<<<<<<<<<<<<<<<<<<<< '); WRITELN(Result); WRITELN(Result,'SAMPLE Serial Number : ',SerialNo,' SAMPLE Run Number : ',RunNo); FOR i := 1 TO NumPeak DO WRITELN(Result,Time[i]:6:2,Peak[i]:12:4); WRITELN(Result,'The resolution is ',Resolution:5:2,' and Peak ',NormPeak:3,' is chosen to be normalized.'); WRITELN(Result); END END; PROCEDURE WriteRef (VAR Result : TEXT; Indata : GcPattern); {This procedure writes the data of a GC pattern to the output file} {which stores the result and its name is given by the user.} {The data includes serial number, run number, the retention time and} {the peak area of each peak.} {A coefficient which indicates the similarity of the patterns is also included.} VAR i : INTEGER; BEGIN WITH Indata DO BEGIN WRITE(Result,'The coefficient is ',Coeff:6:2); WRITELN(Result,' Serial no.: ',SerialNo,' Run no.: ',RunNo); FOR i := 1 TO NumPeak DO WRITELN(Result,Time[i]:6:2,Peak[i]:12:4); WRITELN(Result) END END; PROCEDURE WriteLongResult (VAR LongResult : TEXT; Sample,NormSample : GcPattern; GoodRef1,GoodRef2 : ArrayOfPattern; Resolution : REAL; NormPeak,Count1,Count2 : INTEGER); {This procedure writes the details of the results to the output file} {whose name is given by the user.} {The output data include the serial number, the run number,} {the retention time, the normalized peak area and the coefficient.} {The references are listed in the ascending order of the coefficients.} VAR i : INTEGER; BEGIN IF Count1 > Best THEN Count1 := Best; IF Count2 > Best THEN Count2 := Best; WRITELN (LongResult); WRITELN (LongResult); WriteSample (LongResult,Sample,Resolution,NormPeak); WriteRef (LongResult,NormSample); WRITELN (LongResult); WRITELN (LongResult,'>>Result of Euclidean Distance Test<<'); WRITELN (LongResult); FOR i := 1 TO Count1 DO WriteRef (LongResult,GoodRef1[i]); WRITELN (LongResult); WRITELN (LongResult,'>>Result of Euclidean Distance Test<<'); WRITELN (LongResult); FOR i := 1 TO Count2 DO WriteRef (LongResult,GoodRef2[i]); WRITELN (LongResult); END; PROCEDURE GetData (MaxPeak : INTEGER; VAR Infile : TEXT; VAR Indata : GcPattern; VAR Overflow : BOOLEAN; VAR BadData : BOOLEAN); {This procedure reads the data from the data file one at a time.} {Each set of data consists of serial no., run no., retention time} {and peak area for each peak.} {The procedure will indicate if there is not enough space to read} {all the data in the variable Overflow.} VAR i, IO : INTEGER; BEGIN WITH Indata DO BEGIN READ(Infile,SerialNo,RunNo); i := 0; REPEAT i := i+1; {$I-} READ(Infile,Time[i],Peak[i]); {$I+} IO := IOResult; {! 12. IOResul^t now returns different values corresponding to DOS error codes.} IF IO <> 0 THEN BEGIN WRITELN; WRITELN('Bad Data in the Input File'); WRITELN('Program will terminate for this set of data.'); WRITELN; BadData := TRUE END; UNTIL (Time[i]<0.0) OR (i>=MaxPeak) OR BadData; IF NOT BadData THEN BEGIN IF Time[i] < 0.0 THEN NumPeak := i - 1 ELSE BEGIN WRITELN; WRITELN('>>>>>ARRAY OVERFLOW<<<<<'); WRITELN(SerialNo,' ',RunNo,' has more data than the space allocated.'); WRITELN('The dimension of the array is given by MaxPeak whose present value is ',MaxPeak:3); WRITELN('The program will terminate for this set of data.'); WRITELN; Overflow := TRUE END END END; READLN(Infile) END; PROCEDURE NormalizeSample (Sample : GcPattern; VAR NormSample : GcPattern; VAR NormPeak : INTEGER); {This procedure allows the user to choose one of the peaks} {in the sample pattern to be normalized to one.} VAR i,IO : INTEGER; BEGIN WITH Sample DO BEGIN WRITELN; WRITELN('>>>>>',SerialNo,' ',RunNo,'<<<<<'); FOR i := 1 TO NumPeak DO WRITELN('Peak Number ',i:3,Time[i]:10:2,Peak[i]:10:0); REPEAT NormPeak := 0; WRITELN('Enter the peak number of the peak you want to normalize.'); {$I-} READLN(NormPeak); {$I+} IO := IOResult; {! 13. IOResul^t now returns different values corresponding to DOS error codes.} IF (NormPeak<=0) OR (NormPeak>NumPeak) THEN IO := 10; WHILE IO <> 0 DO BEGIN CheckInteger(NormPeak,IO); IF NormPeak > NumPeak THEN IO := 10 END; UNTIL Peak[NormPeak] > 0.0; WRITELN; WRITE('Peak ',NormPeak:3,' Retention Time ',Time[NormPeak]:6:2,' Peak Area ',Peak[NormPeak]:6:0); WRITELN(' is chosen to be normalized.'); WRITELN('Program is running....'); WRITELN; NormSample.SerialNo := SerialNo; NormSample.RunNo := RunNo; FOR i := 1 TO NumPeak DO BEGIN NormSample.Time[i] := Time[i]; NormSample.Peak[i] := Peak[i]/Peak[NormPeak] END; NormSample.NumPeak := NumPeak END END; PROCEDURE GetRefPeak (Reference,Sample : GcPattern; Resolution : REAL; NormPeak : INTEGER; VAR RefSelect : GcPattern; VAR Match : MatchType; VAR RefOK : BOOLEAN); {This procedure picks out the corresponding peaks in the reference pattern} {so that the sample pattern can be compared to it.} {The procedure to match the peaks is as follows:} { 1. Find the peaks that are within the resolution.} { 2. Pick the one with the smallest time differences.} { 3. Store this matching peak under the variable RefSelect.} {If a peak cannot be matched, the indicator Match will be FALSE.} VAR TimeDiff1, TimeDiff2 : REAL; i,j,k : INTEGER; SmallerDiff : BOOLEAN; BEGIN {Initialize variables} FILLCHAR(RefSelect,SIZEOF(RefSelect),CHR(0)); FOR i := 1 TO MaxPeak DO Match[i] := FALSE; j := 0; k := 0; RefSelect.SerialNo := Reference.SerialNo; RefSelect.RunNo := Reference.RunNo; WITH Sample DO BEGIN FOR i := 1 TO NumPeak DO BEGIN {Set j to the peak that is last picked} j := k; {Pick first peak that falls within the resolution} REPEAT Match[i] := FALSE; j := j+1; TimeDiff1 := Reference.Time[j] - Time[i]; TimeDiff1 := ABS(TimeDiff1); IF TimeDiff1 <= Resolution THEN Match[i] := TRUE UNTIL Match[i] OR (j>=Reference.NumPeak); {Next, pick peak with smallest time difference within resolution} IF Match[i] THEN BEGIN k := j; SmallerDiff := TRUE; WHILE (j0.0) THEN BEGIN RefOk := TRUE; FOR i := 1 TO NumPeak DO Peak[i] := Peak[i]/NormValue END ELSE RefOk := FALSE END END; PROCEDURE GetCoefficient (NormSample,RefSelect : GcPattern; VAR Coeff1,Coeff2 : REAL); {This procedure will compute two coefficients for each reference} {based on two methods.} {The first one is to take the absolute difference of the peak area} {between the sample and the reference.} {The second one is the Euclidean distance, that is} {the square root of the sum of the squares of the difference.} {In this way we can compare the two methods.} VAR i : INTEGER; BEGIN Coeff1 := 0.0; Coeff2 := 0.0; FOR i := 1 TO NormSample.NumPeak DO BEGIN Coeff1 := Coeff1 + ABS(NormSample.Peak[i] - RefSelect.Peak[i]); Coeff2 := Coeff2 + SQR(NormSample.Peak[i] - RefSelect.Peak[i]) END; Coeff2 := SQRT(Coeff2) END; PROCEDURE OrderArray (RefSelect : GcPattern; Coefficient : REAL; VAR GoodRef : ArrayOfPattern; VAR Count : INTEGER); {This procedure will enter the reference into the list of the best references} {in the ascending order of coefficients.} VAR i,j,k : INTEGER; BEGIN i := 1; {Compare new reference to the list of best references} {Locate the appropriate position for this new reference in the list} WHILE (iGoodRef[i].Coeff) DO i := i+1; IF Coefficient <= GoodRef[i].Coeff THEN BEGIN {Push all references with larger coefficients down the list} Count := Count + 1; IF Count > Best THEN Count := Best; FOR j := Count DOWNTO (i+1) DO BEGIN WITH GoodRef[j] DO BEGIN SerialNo := GoodRef[j-1].SerialNo; RunNo := GoodRef[j-1].RunNo; FOR k := 1 TO GoodRef[j-1].NumPeak DO BEGIN Time[k] := GoodRef[j-1].Time[k]; Peak[k] := GoodRef[j-1].Peak[k] END; NumPeak := GoodRef[j-1].NumPeak; Coeff := GoodRef[j-1].Coeff END END; {Insert new reference in the appropriate position of the list} WITH GoodRef[i] DO BEGIN SerialNo := RefSelect.SerialNo; RunNo := RefSelect.RunNo; FOR k := 1 TO RefSelect.NumPeak DO BEGIN Time[k] := RefSelect.Time[k]; Peak[k] := RefSelect.Peak[k] END; NumPeak := RefSelect.NumPeak; Coeff := Coefficient END END {Add new reference to end of list} ELSE BEGIN Count := Count + 1; IF Count <= Best THEN BEGIN WITH GoodRef[Count] DO BEGIN SerialNo := RefSelect.SerialNo; RunNo := RefSelect.RunNo; FOR k := 1 TO RefSelect.NumPeak DO BEGIN Time[k] := RefSelect.Time[k]; Peak[k] := RefSelect.Peak[k] END; NumPeak := RefSelect.NumPeak; Coeff := Coefficient END END ELSE Count := Best END END; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<} {>>>>>>>>>>>>>>> MAIN PROGRAM <<<<<<<<<<<<<<<} {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<} BEGIN {Initialize all variables} FILLCHAR(Reference,SIZEOF(Reference),CHR(0)); FILLCHAR(Sample,SIZEOF(Sample),CHR(0)); FILLCHAR(NormSample,SIZEOF(NormSample),CHR(0)); FILLCHAR(RefSelect,SIZEOF(RefSelect),CHR(0)); FILLCHAR(GoodRef1,SIZEOF(GoodRef1),CHR(0)); FILLCHAR(GoodRef2,SIZEOF(GoodRef2),CHR(0)); FILLCHAR(Match,SIZEOF(Match),CHR(0)); Ready := TRUE; Overflow := FALSE; BadData := FALSE; RefOK := TRUE; Continue := TRUE; Resolution := 0.1; NormPeak := 1; Count1 := 0; Count2 := 0; Coeff1 := 0.0; Coeff2 := 0.0; {End of Initialization} WHILE Continue DO BEGIN Instruction(Ready,Resolution,Best); IF Ready THEN BEGIN {Open the input files containing sample data and reference data} {and the output files to store the results of this program} GetFiles(SmpFile,RefFile,Result,LongResult,SmpName,RefName,Summary,Details,NoDetail); {Compare each set of sample to the list of references} REPEAT FOR index := 1 TO Best DO BEGIN FILLCHAR(GoodRef1,SIZEOF(GoodRef1),CHR(0)); FILLCHAR(GoodRef2,SIZEOF(GoodRef2),CHR(0)) END; Overflow := FALSE; BadData := FALSE; {Reads in sample data one set at a time} GetData(MaxPeak,SmpFile,Sample,Overflow,BadData); IF (NOT Overflow) AND (NOT BadData) THEN BEGIN {Normalize the peaks of the sample data} NormalizeSample(Sample,NormSample,NormPeak); {Count1 keeps track of best references for absolute difference method} {Count2 keeps track of best references for euclidean distance method} Count1 := 0; Count2 := 0; {Compare each reference to sample, compute the 2 coefficients according to} {the two methods used: absolute difference and euclidean distance} {Then place the reference on the best references list accordingly.} WHILE NOT EOF(RefFile) AND (NOT Overflow) AND (NOT BadData) DO BEGIN Overflow := FALSE; BadData := FALSE; GetData(MaxPeak,RefFile,Reference,Overflow,BadData); IF (NOT Overflow) AND (NOT BadData) THEN BEGIN {Get the matching peaks in the reference} GetRefPeak(Reference,NormSample,Resolution,NormPeak,RefSelect,Match,RefOK); {If the peak to be normalized to one has zero area, then reference is} {automatically discarded. RefOK is then set to false.} IF RefOK THEN BEGIN {Normalize the peaks of the reference} NormalizeReference(NormPeak,Match,RefSelect,RefOK); IF RefOK THEN BEGIN {Compute the 2 coefficients according to the two methods used:} { absolute difference and euclidean distance} GetCoefficient(NormSample,RefSelect,Coeff1,Coeff2); {Place the best references in the two lists: GoodRef1, GoodRef2} {The best references will be listed in the ascending order of the coefficients.} OrderArray(RefSelect,Coeff1,GoodRef1,Count1); OrderArray(RefSelect,Coeff2,GoodRef2,Count2) END END END END; CLOSE(RefFile); {Write the summary of the results to the output file on the diskette} WriteResult (Result,Sample,GoodRef1,GoodRef2,Resolution,NormPeak,Count1,Count2); IF NOT NoDetail THEN {Write the details of the results to another output file on the diskette} WriteLongResult (LongResult,Sample,NormSample,GoodRef1,GoodRef2,Resolution,NormPeak,Count1,Count2); {Reset the reference file to compare another set of sample data} ASSIGN(RefFile,RefName); RESET(RefFile) END UNTIL EOF(SmpFile) OR Overflow OR BadData; CLOSE(SmpFile); CLOSE(Result); CLOSE(LongResult) END; IF (NOT Overflow) AND (NOT BadData) THEN BEGIN WRITELN; WRITELN('The summary is stored in the file ',Summary); IF NOT NoDetail THEN WRITELN('The details is stored in the file ',Details); WRITELN END; WRITELN; WRITELN('Do you want to continue with another set of data?'); WRITELN('Enter Y for yes and N for no.'); READLN(Answer); IF Answer IN ['Y','y'] THEN Continue := TRUE ELSE BEGIN Continue := FALSE; WRITELN; WRITELN('User does not wish to continue.'); WRITELN('Program will exit and return to Turbo Pascal.'); WRITELN END END; END. { END OF PROGRAM } END